/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_link.h"
#include <stddef.h>
#include "ola_log.h"
#include "ola_login.h"
#include "ola_error.h"
#include "securec.h"
#include "ola_def.h"
#include "ola_base_utils.h"
#include "ola_service.h"
#include "ola_cloud_token.h"
#define WAIT_RESPONSE_TIME_MAX          (15 * 1000)
#define WAIT_CLOUD_COAP_TIMEVAL         (1 * 1000)


static OlaLinkParam g_linkParam[OLA_LINK_MODE_MAX];

/* 设备方法与对应的处理函数 */
static const OlaCoapResource g_coapResources[] = {
    {OLA_COAP_URI_SERVICES, OLA_COAP_METHOD_TYPE_GET, OlaGetServiceHandler, NULL},
    {OLA_COAP_URI_PROPERTIES_QUERY, OLA_COAP_METHOD_TYPE_POST, OlaQueryDeviceProperties, NULL},
    {OLA_COAP_URI_PROPERTIES_CONTROL, OLA_COAP_METHOD_TYPE_POST, OlaControlDeviceProperties, NULL},
};

int OlaCloudResourceInit(void)
{
    unsigned int index;
    unsigned int len = sizeof(g_coapResources) / sizeof(OlaCoapResource);
    OlaLinkParam *linkParam = OlaGetLinkParam(OLA_LINK_MODE_CLOUD);
    if ((linkParam == NULL) || (linkParam->ctx == NULL)) {
        OlaLogWarning("cloud link param is null");
        return OLA_LINK_ERR_CONTEXT_NULL;
    }
    OlaCoapContext *ctx = linkParam->ctx;
    for (index = 0; index < len; ++index) {
        if (OlaCoapRegisterResource(ctx, &g_coapResources[index]) != OLA_OK) {
            OlaLogError("add coap resource failed");
            return OLA_ERROR;
        }
    }
    return OLA_OK;
}

void OlaLinkInit(void)
{
    (void)memset_s(g_linkParam, sizeof(g_linkParam), 0, sizeof(g_linkParam));
}

OlaLinkParam *OlaGetLinkParam(OlaLinkMode mode)
{
    if (mode >= OLA_LINK_MODE_MAX) {
        OlaLogWarning("invalid mode");
        return NULL;
    }
    return &g_linkParam[mode];
}

bool OlaIsValidCloudSession(const OlaCoapSession *session)
{
    if (session == NULL) {
        OlaLogWarning("invalid param");
        return false;
    }
    OlaLinkParam *linkParam = OlaGetLinkParam(OLA_LINK_MODE_CLOUD);
    if ((linkParam == NULL) || (linkParam->ctx == NULL)) {
        OlaLogWarning("cloud link param is null");
        return false;
    }
    OlaCoapContext *ctx = linkParam->ctx;
    if (session != ctx->session) {
        return false;
    }
    return true;
}

void OlaBuildCloudRequestCoapParam(OlaCoapParam *param, const char *uri,
    OlaCoapResponseHandler handler, bool withToken)
{
    if ((uri == NULL) || (param == NULL)) {
        OlaLogWarning("invalid param");
        return;
    }
    param->type = OLA_COAP_PACKET_TYPE_REQUEST;
    param->method = OLA_COAP_METHOD_TYPE_POST;
    param->handler = handler;
    param->optionsNum = withToken ? 3 : 2;
    param->options[0].option = OLA_COAP_OPTION_TYPE_URI_PATH;
    param->options[0].buf.data = (unsigned char *)uri;
    param->options[0].buf.len = strlen(uri);
    param->options[1].option = OLA_COAP_OPTION_TYPE_CONTENT_FORMAT;
    param->options[1].buf.data = (unsigned char *)OLA_COAP_OPTION_JSON;
    param->options[1].buf.len = strlen(OLA_COAP_OPTION_JSON);
    if (withToken) {
        const char *accessToken = CloudGetAccessToken();
        if (accessToken == NULL) {
            OlaLogWarning("get token error");
            return;
        }
        if ((strlen(accessToken) == 0) ||(strlen(accessToken) > OLA_LOGIN_ACCESS_TOKEN_LEN)) {
            OlaLogWarning("token invalid");
            return;
        }
        param->options[2].option = OLA_COAP_OPTION_TYPE_ACCESS_TOKEN_ID;
        param->options[2].buf.data = (unsigned char *)accessToken;
        param->options[2].buf.len = strlen(accessToken);
    }
}

int OlaSendDataToCloud(const OlaData *data, const OlaCoapParam *param)
{
    if ((data == NULL) ||(param == NULL) || (data->data == NULL) || (data->len == 0)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    int ret;
    /* 获取云端会话上下文 */
    OlaLinkParam *linkParam = OlaGetLinkParam(OLA_LINK_MODE_CLOUD);
    if ((linkParam == NULL) || (linkParam->ctx == NULL)) {
        OlaLogWarning("cloud link param is null");
        return OLA_LINK_ERR_CONTEXT_NULL;
    }
    OlaCoapContext *ctx = linkParam->ctx;

    ret = OlaCoapSendData(ctx->session, data, param);
    if (ret != OLA_OK) {
        OlaLogWarning("send to cloud error");
    }
    return ret;
}

int OlaWaitCloudResponse(unsigned long long *startTime)
{
    if (startTime == NULL) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    int ret;
    if (*startTime == 0) {
        ret = OlaGetTime(startTime);
        if ((ret != 0) || (*startTime == 0)) {
            OlaLogError("get time error[%d]", ret);
            return OLA_OS_ERR_GET_TIME;
        }
    }
    bool isOvertime = false;
    ret = OlaIsOvertime(&isOvertime, *startTime, WAIT_RESPONSE_TIME_MAX);
    if (ret != OLA_OK) {
        OlaLogError("check overtime error");
        return ret;
    }
    if (isOvertime) {
        OlaLogWarning("wait register resp overtime");
        return OLA_CONNECT_ERR_RESPONSE_OVER_TIME;
    }
    return OlaCoapProcess(WAIT_CLOUD_COAP_TIMEVAL);
}